package com.lncg.handler;

import com.lncg.entity.DeviceReceiveData;
import com.lncg.service.IDeviceReceiveDataService;
import com.lncg.service.ITcpConnectionService;
import com.lncg.service.DeviceSendDataService;
import com.lncg.service.ICqzlDeviceReceiveDataService;
import com.lncg.util.LncgProtocolParser;
import com.lncg.util.CqzlProtocolParser;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;

/**
 * 设备数据处理器
 * 
 * 该类负责处理来自TCP客户端的设备数据，支持两种协议：
 * 
 * 1. LNCG协议（92字节固定长度数据包）：
 *    - 接收和解析92字节的固定长度数据包
 *    - 验证数据包的CRC16校验
 *    - 将解析后的数据保存到数据库
 *    - 自动生成回复指令并发送给前端
 * 
 * 2. CQZL协议（JSON格式数据）：
 *    - 接收JSON格式的设备数据
 *    - 解析CQZL协议字段
 *    - 执行UPSERT操作到主表
 *    - 插入历史记录到历史表
 * 
 * 其他功能：
 * - 管理TCP客户端连接映射
 * - 处理连接的生命周期事件（连接建立、断开、空闲等）
 * - 处理32字节确认指令
 * 
 * @author LNCG
 * @version 1.1.0
 * @since 2025-09-25
 */
@Component
@ChannelHandler.Sharable  // 允许在多个Channel之间共享此Handler实例，提高性能
public class DeviceDataHandler extends ChannelInboundHandlerAdapter {
    
    private static final Logger logger = LoggerFactory.getLogger(DeviceDataHandler.class);
    
    /**
     * 设备接收数据服务（数据库持久化）
     */
    @Autowired
    private IDeviceReceiveDataService deviceReceiveDataService;
    
    /**
     * CQZL设备数据专用服务
     */
    @Autowired
    private ICqzlDeviceReceiveDataService cqzlDeviceReceiveDataService;
    
    /**
     * 协议解析器，负责将原始字节数据解析为结构化的设备数据对象
     * 按照92字节的固定协议格式进行解析
     */
    @Autowired
    private LncgProtocolParser protocolParser;
    
    @Autowired
    private CqzlProtocolParser cqzlProtocolParser;
    

    /**
     * TCP连接管理服务，用于管理TCP连接
     */
    @Autowired
    private ITcpConnectionService tcpConnectionService;

    // 事件机制已移除

    /**
     * 设备下发数据服务，用于处理下发数据和更新发送状态
     */
    @Autowired
    private DeviceSendDataService deviceSendDataService;
    
    
    /**
     * 当有数据到达时调用
     * 
     * 这是Netty ChannelHandler的核心方法，负责处理来自TCP客户端的数据包。
     * 支持两种协议格式：
     * 
     * 1. LNCG协议：92字节固定长度数据包
     * 2. CQZL协议：JSON格式数据包
     * 
     * 处理流程：
     * 1. 验证数据包类型和长度
     * 2. 提取客户端连接信息（IP和端口）
     * 3. 读取原始字节数据
     * 4. 根据协议类型分发处理
     * 5. 解析协议数据并保存到数据库
     * 6. 生成并发送回复指令（LNCG协议）
     * 
     * @param ctx Netty Channel上下文，包含连接信息
     * @param msg 接收到的消息对象，应该是ByteBuf类型
     * @throws Exception 处理过程中的异常
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof ByteBuf) {
            ByteBuf buffer = (ByteBuf) msg;
            try {
                // 获取客户端IP地址和端口信息
                InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
                String clientIp = address.getAddress().getHostAddress();
                int clientPort = address.getPort();
                
                logger.info("接收数据 {}:{} 长度:{}", clientIp, clientPort, buffer.readableBytes());
                
                // 检测数据格式并分发处理
                int dataLength = buffer.readableBytes();
                
                // 首先检查所有标准协议格式
                if (dataLength == 92 && is92ByteProtocol(buffer)) {
                    // 处理92字节LNCG协议上行数据指令
                    handleDataCommand(ctx, buffer, clientIp, clientPort);
                } else if (dataLength == 32 && is32ByteProtocol(buffer)) {
                    // 处理32字节LNCG协议上行确认指令
                    handleConfirmResponse(ctx, buffer, clientIp, clientPort);
                } else if (isCqzlProtocol(buffer)) {
                    // 处理CQZL协议数据
                    handleCqzlProtocol(ctx, buffer, clientIp, clientPort);
                } else {
                    logger.warn("不支持的数据格式 长度:{} 客户端:{}:{}", dataLength, clientIp, clientPort);
                    return;
                }
                
            } catch (Exception e) {
                logger.error("处理设备数据异常: {}", e.getMessage(), e);
            } finally {
                // 释放ByteBuf资源，防止内存泄漏
                buffer.release();
            }
        } else {
            logger.warn("接收到非ByteBuf类型的数据: {}", msg.getClass().getName());
        }
    }
    
    /**
     * 当TCP客户端连接建立时调用
     * 
     * 此方法在TCP客户端成功连接到服务器时被触发。
     * 主要功能：
     * 1. 获取客户端连接信息（IP地址和端口）
     * 2. 将连接信息添加到连接映射表中
     * 3. 记录连接建立的日志
     * 4. 更新当前连接数统计
     * 
     * @param ctx Netty Channel上下文
     * @throws Exception 处理过程中的异常
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = address.getAddress().getHostAddress();
        int clientPort = address.getPort();
        String clientKey = clientIp + ":" + clientPort;

        // 将连接添加到连接管理服务中
        tcpConnectionService.addConnection(clientKey, ctx);

        logger.info("客户端连接 {}:{} 连接数:{}", clientIp, clientPort, tcpConnectionService.getConnectionCount());
        super.channelActive(ctx);
    }
    
    /**
     * 当TCP客户端连接断开时调用
     * 
     * 此方法在TCP客户端断开连接时被触发。
     * 主要功能：
     * 1. 获取断开连接的客户端信息
     * 2. 从连接映射表中移除该连接
     * 3. 记录连接断开的日志
     * 4. 更新当前连接数统计
     * 
     * @param ctx Netty Channel上下文
     * @throws Exception 处理过程中的异常
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = address.getAddress().getHostAddress();
        int clientPort = address.getPort();
        String clientKey = clientIp + ":" + clientPort;

        // 从连接管理服务中移除已断开的连接
        tcpConnectionService.removeConnection(clientKey);

        logger.info("客户端断开 {}:{} 连接数:{}", clientIp, clientPort, tcpConnectionService.getConnectionCount());
        super.channelInactive(ctx);
    }
    
    /**
     * 处理空闲状态事件
     * 
     * 当客户端连接进入空闲状态时被触发，用于检测和处理连接超时。
     * 根据配置的空闲超时时间，自动关闭长时间无活动的连接。
     * 
     * 空闲状态类型：
     * - READER_IDLE: 读空闲（客户端长时间未发送数据）
     * - WRITER_IDLE: 写空闲（服务器长时间未发送数据）
     * - ALL_IDLE: 读写空闲（双向长时间无数据交换）
     * 
     * @param ctx Netty Channel上下文
     * @param evt 触发的事件对象
     * @throws Exception 处理过程中的异常
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
            String clientIp = address.getAddress().getHostAddress();
            int clientPort = address.getPort();
            String clientKey = clientIp + ":" + clientPort;
            
            if (event.state() == IdleState.READER_IDLE) {
                logger.warn("读空闲超时 关闭连接 {}:{}", clientIp, clientPort);
                // 主动从连接管理中移除
                tcpConnectionService.removeConnection(clientKey);
                ctx.close();
            } else if (event.state() == IdleState.WRITER_IDLE) {
                logger.warn("写空闲超时 {}:{}", clientIp, clientPort);
            } else if (event.state() == IdleState.ALL_IDLE) {
                logger.warn("读写空闲超时 关闭连接 {}:{}", clientIp, clientPort);
                // 主动从连接管理中移除
                tcpConnectionService.removeConnection(clientKey);
                ctx.close();
            }
        }
        super.userEventTriggered(ctx, evt);
    }
    
    /**
     * 当发生异常时调用
     * 
     * 当处理客户端数据或连接过程中发生异常时被触发。
     * 记录详细的异常信息并关闭有问题的连接，防止异常传播。
     * 
     * @param ctx Netty Channel上下文
     * @param cause 异常对象
     * @throws Exception 处理过程中的异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = address.getAddress().getHostAddress();
        int clientPort = address.getPort();
        
        // 根据异常类型进行不同级别的日志记录
        String errorType = cause.getClass().getSimpleName();
        String errorMessage = cause.getMessage();
        
        if (errorMessage != null && errorMessage.contains("Connection reset")) {
            // 客户端主动断开连接，记录为INFO级别
            logger.info("客户端主动断开连接 {}:{} 原因:{}", clientIp, clientPort, errorMessage);
        } else if (errorMessage != null && errorMessage.contains("Broken pipe")) {
            // 管道破裂，通常是网络问题
            logger.warn("网络连接异常 {}:{} 原因:{}", clientIp, clientPort, errorMessage);
        } else {
            // 其他异常，记录为ERROR级别
            logger.error("处理客户端数据异常 {}:{} 类型:{} 错误:{}", clientIp, clientPort, errorType, errorMessage);
        }
        
        // 关闭有异常的连接，防止影响其他连接
        ctx.close();
    }
    
    /**
     * 获取当前TCP连接数
     * 
     * 返回当前活跃的TCP客户端连接数量。
     * 用于监控和统计目的。
     * 
     * @return 当前TCP连接数
     */
    public int getTcpConnectionCount() {
        return tcpConnectionService.getConnectionCount();
    }

    /**
     * 检测是否为92字节协议
     * 
     * @param buffer 数据缓冲区
     * @return true如果是92字节协议
     */
    private boolean is92ByteProtocol(ByteBuf buffer) {
        if (buffer.readableBytes() < 3) {
            return false;
        }
        // 检查前导符 0x43 0x47
        byte[] prefix = new byte[2];
        buffer.getBytes(buffer.readerIndex(), prefix);
        return prefix[0] == 0x43 && prefix[1] == 0x47;
    }
    
    /**
     * 检测是否为32字节协议
     * 
     * @param buffer 数据缓冲区
     * @return true如果是32字节协议
     */
    private boolean is32ByteProtocol(ByteBuf buffer) {
        if (buffer.readableBytes() < 3) {
            return false;
        }
        // 检查前导符 0x43 0x47
        byte[] prefix = new byte[2];
        buffer.getBytes(buffer.readerIndex(), prefix);
        return prefix[0] == 0x43 && prefix[1] == 0x47;
    }
    
    
    /**
     * 检测是否为CQZL协议
     * 
     * @param buffer 数据缓冲区
     * @return true如果是CQZL协议
     */
    private boolean isCqzlProtocol(ByteBuf buffer) {
        if (buffer.readableBytes() < 2) {
            return false;
        }
        // 检查前导符 0x7B
        byte[] prefix = new byte[1];
        buffer.getBytes(buffer.readerIndex(), prefix);
        return prefix[0] == 0x7B;
    }
    

    /**
     * 处理LNCG协议92字节上行数据指令
     * 
     * 处理LNCG协议表端上报的数据，包括解析、验证、存储和下发逻辑。
     * 
     * @param ctx Netty Channel上下文
     * @param buffer 数据缓冲区
     * @param clientIp 客户端IP
     * @param clientPort 客户端端口
     */
    private void handleDataCommand(ChannelHandlerContext ctx, ByteBuf buffer, String clientIp, int clientPort) {
        try {
            // 读取92字节数据
            byte[] data = new byte[92];
            buffer.readBytes(data);
            
            String hexData = bytesToHex(data);
            logger.info("上行数据指令: {}", hexData);
            
            // 使用协议解析器将原始字节数据解析为结构化的设备数据对象
            DeviceReceiveData deviceData = protocolParser.parseProtocol(data);
            
            // 设置接收时间
            deviceData.setReciveTime(java.time.LocalDateTime.now().toString());
            
            logger.info("协议解析成功 IMEI:{}", deviceData.getImei());
            
            // 验证CRC16校验码（仅作为参考，不阻断数据处理）
            // CRC16从第3位开始至第89位，共87字节（跳过前导符）
            byte[] dataForCrc = new byte[87];
            System.arraycopy(data, 2, dataForCrc, 0, 87);
            byte[] receivedCrc = new byte[2];
            System.arraycopy(data, 89, receivedCrc, 0, 2);
            
            boolean crcValid = LncgProtocolParser.verifyCrc16(dataForCrc, receivedCrc);
            if (!crcValid) {
                logger.warn("CRC16校验未通过 {}:{}", clientIp, clientPort);
            }
            
            // 同步持久化到数据库，确保更新完成后再检查待下发数据
            deviceReceiveDataService.processDeviceData(deviceData);
            // 落库完成

            // 入库完成后检查是否有待下发数据并处理（返回true表示已执行下发数据，false表示无待下发数据或仅发送确认）
            try {
                boolean dispatched = deviceSendDataService.processAfterReceive(deviceData, clientIp, clientPort);
                if (dispatched) {
                    logger.info("处理待下发数据成功 IMEI:{}", deviceData.getImei());
                } else {
                    logger.info("无待下发数据，已发送确认 IMEI:{}", deviceData.getImei());
                }
            } catch (Exception ex) {
                logger.error("处理待下发数据失败 IMEI:{} 错误:{}", deviceData.getImei(), ex.getMessage());
            }

        } catch (Exception e) {
            logger.error("处理92字节上行数据指令失败: {}", e.getMessage(), e);
        }
    }
    
    
    /**
     * 处理CQZL协议数据
     * 
     * 处理CQZL格式的设备数据，使用专用的CQZL服务进行UPSERT和插入历史表操作。
     * 
     * @param ctx Netty Channel上下文
     * @param buffer 数据缓冲区
     * @param clientIp 客户端IP
     * @param clientPort 客户端端口
     */
    private void handleCqzlProtocol(ChannelHandlerContext ctx, ByteBuf buffer, String clientIp, int clientPort) {
        try {
            // 读取所有CQZL数据
            byte[] data = new byte[buffer.readableBytes()];
            buffer.readBytes(data);

            // 转换为JSON字符串
            String jsonData = new String(data, java.nio.charset.StandardCharsets.UTF_8);
            logger.info("CQZL数据（JSON格式）: {}", jsonData);
            
            // 使用专用的CQZL服务处理数据（包含UPSERT和插入历史表）
            String clientIpPort = clientIp + ":" + clientPort;
            String result = cqzlDeviceReceiveDataService.processCqzlData(jsonData, clientIpPort);
            logger.info("CQZL数据处理结果: {}", result);
            
            // CQZL协议不需要发送确认响应，直接关闭连接
            ctx.close();
            
        } catch (Exception e) {
            logger.error("处理CQZL协议数据失败: {}", e.getMessage(), e);
            ctx.close();
        }
    }

    /**
     * 处理LNCG协议32字节上行确认指令
     * 
     * 处理LNCG协议表端回复的确认消息，用于确认下发数据是否成功接收。
     * 收到确认后断开连接并更新数据库发送状态。
     * 
     * @param ctx Netty Channel上下文
     * @param buffer 数据缓冲区
     * @param clientIp 客户端IP
     * @param clientPort 客户端端口
     */
    private void handleConfirmResponse(ChannelHandlerContext ctx, ByteBuf buffer, String clientIp, int clientPort) {
        try {
            // 读取32字节数据
            byte[] data = new byte[32];
            buffer.readBytes(data);
            
            String hexData = bytesToHex(data);
            logger.info("上行确认指令: {}", hexData);
            
            // 解析确认响应
            DeviceSendDataService.ConfirmResponse response = parseConfirmResponse(data);
            
            if (response.isValid()) {
                logger.info("确认指令解析成功 指令编号:{} IMEI:{}", response.getInstructionId(), response.getImei());
                
                try {
                    deviceSendDataService.updateSendStatusByInstructionId(response.getInstructionId(), 1);
                } catch (Exception e) {
                    logger.error("更新发送状态失败 指令编号:{} 错误:{}", response.getInstructionId(), e.getMessage());
                }
                
                tcpConnectionService.closeConnection(clientIp, clientPort);
            } else {
                logger.warn("确认指令解析失败 {}:{}", clientIp, clientPort);
            }
        } catch (Exception e) {
            logger.error("处理32字节上行确认指令失败: {}", e.getMessage(), e);
        }
    }

    /**
     * 解析LNCG协议32字节上行确认指令
     * 
     * @param data 32字节数据
     * @return 解析结果
     */
    private DeviceSendDataService.ConfirmResponse parseConfirmResponse(byte[] data) {
        try {
            if (data == null || data.length != 32) {
                logger.warn("上行确认指令长度不正确，期望32字节，实际{}字节", data != null ? data.length : 0);
                return new DeviceSendDataService.ConfirmResponse("", "", false);
            }
            
            // 简单的前导符和结束符验证
            if (data[0] != 0x43 || data[1] != 0x47) {
                logger.warn("上行确认指令前导符错误");
                return new DeviceSendDataService.ConfirmResponse("", "", false);
            }
            
            if (data[31] != 0x16) {
                logger.warn("上行确认指令结束符错误");
                return new DeviceSendDataService.ConfirmResponse("", "", false);
            }
            
            // 提取指令编号（第3-12字节）
            byte[] instructionBytes = new byte[10];
            System.arraycopy(data, 2, instructionBytes, 0, 10);
            // 将字节数组转换为十六进制字符串，保持原始格式
            StringBuilder sb = new StringBuilder();
            for (byte b : instructionBytes) {
                sb.append(String.format("%02X", b & 0xFF));
            }
            String instructionId = sb.toString();
            
            // 检查指令类型（第13字节，应该是0x04）
            if (data[12] != 0x04) {
                logger.warn("上行确认指令类型错误，期望0x04，实际0x{}", String.format("%02X", data[12] & 0xFF));
                return new DeviceSendDataService.ConfirmResponse(instructionId, "", false);
            }
            
            // 提取IMEI（第14-29字节）
            String imei = new String(data, 13, 16, StandardCharsets.US_ASCII).trim();
            
            logger.info("上行确认指令解析成功，指令编号: {}, IMEI: {}", instructionId, imei);
            return new DeviceSendDataService.ConfirmResponse(instructionId, imei, true);
            
        } catch (Exception e) {
            logger.error("解析上行确认指令失败: {}", e.getMessage(), e);
            return new DeviceSendDataService.ConfirmResponse("", "", false);
        }
    }

    /**
     * 字节数组转十六进制字符串
     * 
     * 将字节数组转换为十六进制字符串，便于调试和日志记录。
     * 每个字节转换为两位十六进制字符。
     * 
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02X", b & 0xFF));
        }
        return result.toString();
    }
    
    /**
     * 十六进制字符串转字节数组
     * 
     * 将十六进制字符串转换为字节数组。
     * 支持带空格分隔或不带分隔符的十六进制字符串。
     * 
     * @param hexString 十六进制字符串（如："7B 22 74 73" 或 "7B227473"）
     * @return 字节数组
     */
    private byte[] hexStringToBytes(String hexString) {
        // 移除所有空格和换行符
        String cleanHex = hexString.replaceAll("\\s+", "");
        
        // 确保长度为偶数
        if (cleanHex.length() % 2 != 0) {
            cleanHex = "0" + cleanHex;
        }
        
        byte[] bytes = new byte[cleanHex.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            String hexByte = cleanHex.substring(i * 2, i * 2 + 2);
            bytes[i] = (byte) Integer.parseInt(hexByte, 16);
        }
        return bytes;
    }
    
    /**
     * 十六进制字符串转ASCII字符串
     * 
     * 将十六进制字符串转换为ASCII字符串。
     * 支持带空格分隔或不带分隔符的十六进制字符串。
     * 
     * @param hexString 十六进制字符串（如："48 65 6C 6C 6F" 或 "48656C6C6F"）
     * @return ASCII字符串（如："Hello"）
     */
    private String hexStringToAscii(String hexString) {
        byte[] bytes = hexStringToBytes(hexString);
        return new String(bytes, StandardCharsets.US_ASCII);
    }
    
}
